package slhs.tempo.scriptGenerator;

import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.swing.DefaultListModel;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;

import org.mlc.swing.layout.LayoutConstraintsManager;

import slhs.tempo.TableAnalyzer.TableColumnAdjuster;
import slhs.tempo.TableAnalyzer.TableStateMemento;
import slhs.tempo.dataImporterUtilities.OutFileFields;

public class UserAssistedParserPanelHandler extends JPanel implements PanelHandler {
    private static final String PARSER_PANEL_HANDLER_LAYOUT_XML_FILE = "PARSER_PANEL_HANDLER_LAYOUT.xml";
    private static final String SPACE = " ";
    private static final String SPACE_DELIMITER_IDENTIFIER = "<SPACE>";
    private static final String BLANK = "";
    private static final int FIELDS_COLUMN_INDEX = 0;
    private int longestLineLength = 0;
    private Vector longestRow;
    private boolean firstUndo = true;
    private static final String[] coloumns = { "Field", "Coloumn Title" };
    private final String[] defaultColoumns = { "Subject", "Status", "Wave File", "Probe", "Probe Delay",
	    "Reaction Time", "Response", "Correct Response", "VRDelay", "Blink", "Prac/Fill/Exp", "Type", "Ctl/Rel",
	    "Item Number", "Stimulus Number", "Probe Point", "Script" };
    private final String[] blankRow = { "", "" };

    private final String[] delimiters = { ";" };

    private JLabel separatorsLabel;

    private JList delimiterList;
    private DefaultListModel listModel;

    private JTextField delimeterTextField;

    private JButton addDelimeterButton;
    private JButton undoButton;
    private JButton okButton;
    private JButton cancelButton;

    private JScrollPane coloumnsTableScrollPane;
    private JScrollPane delimeterListScrollPane;

    private JTable coloumnsTable;
    private DefaultTableModel tableModel;

    TableStateMemento memento;

    private final JPanel userAssistedParserPanel;
    private final ModeSelectorScreenHandler modeSelectorScreenHandler;
    private final OutFileFields[] outFileFieldsArray;

    /************************************************************************
     * Class constructor - start
     ************************************************************************/
    public UserAssistedParserPanelHandler(ModeSelectorScreenHandler modeSelectorScreenHandler,
	    OutFileFields[] outFileFieldsArray) {
	this.modeSelectorScreenHandler = modeSelectorScreenHandler;
	this.outFileFieldsArray = outFileFieldsArray;
	userAssistedParserPanel = new JPanel();

	GroupLayout parserPanelLayout = new GroupLayout(userAssistedParserPanel);
	userAssistedParserPanel.setLayout(parserPanelLayout);

	initComponents();
	addComponents();

    }

    /************************************************************************
     * Class constructor - End
     ************************************************************************/

    /************************************************************************
     * InitComponents method - start
     ************************************************************************/
    private void initComponents() {
	addDelimeterButton = new JButton("Add");
	addDelimeterButton.addActionListener(this);

	undoButton = new JButton("Undo");
	undoButton.addActionListener(this);

	okButton = new JButton("Ok");
	okButton.addActionListener(this);

	cancelButton = new JButton("Cancel");
	cancelButton.addActionListener(this);

	separatorsLabel = new JLabel("Separators:");

	delimeterTextField = new JTextField(5);

	memento = new TableStateMemento();

	initListModel();
	initTable();

    }

    /************************************************************************
     * InitComponents method - end
     ************************************************************************/

    /************************************************************************
     * Add Components method - start
     ************************************************************************/
    private void addComponents() {
	LayoutConstraintsManager layoutConstraintsManager = LayoutConstraintsManager.getLayoutConstraintsManager(this
		.getClass().getResourceAsStream(PARSER_PANEL_HANDLER_LAYOUT_XML_FILE));
	LayoutManager panelLayout = layoutConstraintsManager.createLayout("panel", this);
	userAssistedParserPanel.setLayout(panelLayout);

	userAssistedParserPanel.add(addDelimeterButton, "addDelimeterButton");
	userAssistedParserPanel.add(undoButton, "undoButton");
	userAssistedParserPanel.add(separatorsLabel, "separatorsLabel");
	userAssistedParserPanel.add(coloumnsTableScrollPane, "coloumnsTable");
	userAssistedParserPanel.add(delimeterTextField, "delimeterTextField");
	userAssistedParserPanel.add(delimeterListScrollPane, "delimeterList");
	userAssistedParserPanel.add(cancelButton, "cancelButton");
	userAssistedParserPanel.add(okButton, "okButton");

    }

    /************************************************************************
     * AddComponents method - end
     ************************************************************************/

    /************************************************************************
     * Event listeners - start
     ************************************************************************/
    @Override
    public void actionPerformed(ActionEvent event) {
	Object source = event.getSource();

	/************************************************************************
	 * Event listeners - add delimiter button - Start
	 ************************************************************************/
	/* Add button actions */
	if (source.equals(addDelimeterButton)) {

	    /* Alter and update table */
	    String delimeterToAdd = delimeterTextField.getText();

	    if (!delimeterToAdd.equals(null) && !delimeterToAdd.equals(BLANK) && !(listModel.contains(delimeterToAdd))) {
		/* Since space is not visible in the list */
		if (delimeterToAdd.equals(SPACE)) {
		    listModel.addElement(SPACE_DELIMITER_IDENTIFIER);
		} else
		    listModel.addElement(delimeterToAdd);
	    }
	    /* reset the user input text field to blank again */
	    delimeterTextField.setText(BLANK);
	    updateTable();

	    /* Deep copy table data vector to stack */
	    Vector newDataVector = tableModel.getDataVector();
	    memento.saveCurrentState(newDataVector);
	}
	/************************************************************************
	 * Event listeners - add delimiter button - Start
	 ************************************************************************/

	/************************************************************************
	 * Event listeners - undo button - Start
	 ************************************************************************/
	/* Undo button action */
	if (source.equals(undoButton)) {

	    if (firstUndo) {
		memento.getLastState();
		firstUndo = false;
	    }

	    /* delete the selected delimiter from the list */
	    if (listModel.size() > 1) {

		listModel.removeElementAt(listModel.size() - 1);
		Object previousTableDataVector = memento.getLastState();
		tableModel.setDataVector((Vector) previousTableDataVector, new Vector<String>(Arrays.asList(coloumns)));

	    }

	    else
		JOptionPane.showMessageDialog(this, "At least one delimiter is required", "Warning",
			JOptionPane.WARNING_MESSAGE);

	}

	/************************************************************************
	 * Event listeners - undo button - End
	 ************************************************************************/

	/************************************************************************
	 * Event listeners - Ok button - start
	 ************************************************************************/
	if (source.equals(okButton)) {

	    int numberOfRows = tableModel.getRowCount();
	    Vector<Object> columnVector = new Vector<Object>();
	    for (int rowIndex = 0; rowIndex < numberOfRows; rowIndex++) {
		columnVector.add(tableModel.getValueAt(rowIndex, 1));
	    }
	    /* Go through the table data and parse it with new delimiters */
	    splitTableRows();

	    modeSelectorScreenHandler.createTable(columnVector);

	}

	/************************************************************************
	 * Event listeners - Ok button - End
	 ************************************************************************/

	/************************************************************************
	 * Event listeners - cancel button - start
	 ************************************************************************/
	if (source.equals(cancelButton)) {
	    System.exit(0);
	}
	/************************************************************************
	 * Event listeners - cancel button - End
	 ************************************************************************/

    }

    /************************************************************************
     * Event listeners - End
     ************************************************************************/

    /************************************************************************
     * SplitTableRows method - start
     ************************************************************************/
    private void splitTableRows() {

	for (int delimiterIndex = 0; delimiterIndex < listModel.size(); delimiterIndex++) {
	    for (int fileCount = 0; fileCount < outFileFieldsArray.length; fileCount++) {
		OutFileFields outFileFields = outFileFieldsArray[fileCount];
		ArrayList<Vector> row = outFileFields.getData();

		int totalNumberOfRows = outFileFields.getNumberOfRows();
		for (int rowIndex = 0; rowIndex < totalNumberOfRows; rowIndex++) {
		    Vector oldVector = outFileFields.getRow(rowIndex);
		    Vector newVector = new Vector();
		    Iterator it = oldVector.iterator();
		    while (it.hasNext()) {
			String currentVectorElement = (String) it.next();
			String currentDelimiter = listModel.elementAt(delimiterIndex).toString();
			currentDelimiter = currentDelimiter.equals(SPACE_DELIMITER_IDENTIFIER) ? SPACE
				: currentDelimiter;
			String[] parsedVectorElement = currentVectorElement.split(currentDelimiter);
			for (int i = 0; i < parsedVectorElement.length; i++) {
			    newVector.add(parsedVectorElement[i]);
			}

		    }
		    outFileFields.setRow(rowIndex, newVector);
		}

	    } /* rowCount loop ends here */
	} /* delimiterIndex loop ends here */
    } /* splitTableRows methos ends here */

    /************************************************************************
     * SplitTableRows method - end
     ************************************************************************/

    /************************************************************************
     * Update table method - start
     ************************************************************************/

    private void updateTable() {

	ArrayList fieldNames = getFieldNames();
	ArrayList<String> delimiterList = getDelimiterList();
	String newDelimiter = delimiterList.get(delimiterList.size() - 1).toString();
	ArrayList<String> newColumnList = new ArrayList<String>(fieldNames.size());
	// System.out.print("New delimeter:\t" + newDelimiter);

	for (int columnIndex = 0; columnIndex < fieldNames.size(); columnIndex++) {
	    // split each column based on new delimiter
	    String[] delimitedColumn = fieldNames.get(columnIndex).toString().split(newDelimiter);

	    // Add rows to table to account for newly added column names
	    int rowsAdded = delimitedColumn.length - 1;
	    for (int numberOfRows = rowsAdded; numberOfRows > 0; numberOfRows--) {
		tableModel.insertRow(columnIndex + numberOfRows, blankRow);
	    }

	    List<String> delimitedList = Arrays.asList(delimitedColumn);
	    newColumnList.addAll(delimitedList);

	}

	// System.out.println("New columnList");
	Iterator<String> it = newColumnList.iterator();
	for (int rowIndex = 0; rowIndex < newColumnList.size(); rowIndex++)
	    tableModel.setValueAt(it.next(), rowIndex, FIELDS_COLUMN_INDEX);
	TableColumnAdjuster tca = new TableColumnAdjuster(coloumnsTable);
	tca.adjustColumns();

    }

    /************************************************************************
     * Update table method - end
     ************************************************************************/

    /************************************************************************
     * Get DelimiterList method - start
     ************************************************************************/
    private ArrayList<String> getDelimiterList() {
	int delimeterCount = listModel.getSize();
	ArrayList<String> listData = new ArrayList<String>(delimeterCount);

	/* get all the delimeters */
	for (int listIndex = 0; listIndex < delimeterCount; listIndex++) {
	    String currentDelimiter = (String) listModel.elementAt(listIndex);
	    if (currentDelimiter.equals(SPACE_DELIMITER_IDENTIFIER))
		listData.add(SPACE);
	    else
		listData.add(currentDelimiter);
	}
	return listData;
    }

    /************************************************************************
     * Get DelimiterList method - end
     ************************************************************************/

    /************************************************************************
     * GetFieldNames method - start
     ************************************************************************/
    private ArrayList<String> getFieldNames() {
	int rowCount = coloumnsTable.getRowCount();
	Vector tableData = tableModel.getDataVector();
	ArrayList<String> columnData = new ArrayList<String>(rowCount);

	/* get column data */
	for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
	    Vector<String> row = (Vector<String>) tableData.elementAt(rowIndex);
	    columnData.add(row.get(FIELDS_COLUMN_INDEX));
	    // System.out.println("Adding: "+row.get(FIELDS_COLUMN_INDEX).toString()+" to columnData");
	}
	return columnData;
    }

    /************************************************************************
     * GetFieldNames method - end
     ************************************************************************/

    /************************************************************************
     * GetColumnNames method - start
     ************************************************************************/
    protected Vector getColumnNames(OutFileFields outFileFields) {
	Vector row = outFileFields.getRow(0);
	return row;
    }

    /************************************************************************
     * GetColumnNames method - end
     ************************************************************************/

    /************************************************************************
     * InitListModel method - start
     ************************************************************************/

    private void initListModel() {
	listModel = new DefaultListModel();

	for (int listIndex = 0; listIndex < delimiters.length; listIndex++) {
	    listModel.addElement(delimiters[listIndex]);
	}
	delimiterList = new JList(listModel);
	delimeterListScrollPane = new JScrollPane(delimiterList);
    }

    /************************************************************************
     * InitListModel method - start
     ************************************************************************/

    /************************************************************************
     * InitTable method - start
     ************************************************************************/
    private void initTable() {
	tableModel = new DefaultTableModel();
	for (int columnIndex = 0; columnIndex < coloumns.length; columnIndex++) {
	    tableModel.addColumn(coloumns[columnIndex]);
	}

	// get the longest row among all the files
	for (int fileIndex = 0; fileIndex < outFileFieldsArray.length; fileIndex++) {
	    OutFileFields outFileFields = outFileFieldsArray[fileIndex];
	    int numberOfRows = outFileFields.getNumberOfRows();
	    // System.out.println("OutFileFields has:\t" + numberOfRows);
	    for (int rowNumber = 0; rowNumber < numberOfRows; rowNumber++) {
		try {
		    Vector row = outFileFields.getRow(rowNumber);
		    if (row.size() > longestLineLength) {
			longestLineLength = row.size();
			longestRow = row;
		    }
		} catch (Exception e) {
		    e.printStackTrace();
		}

	    }

	}

	for (int i = 0; i < longestRow.size(); i++) {
	    tableModel.addRow(coloumns);
	}
	for (int i = 0; i < longestRow.size(); i++) {
	    tableModel.setValueAt(longestRow.get(i), i, 0);
	    try {
		tableModel.setValueAt(defaultColoumns[i], i, 1);
	    } catch (ArrayIndexOutOfBoundsException e) {
		tableModel.setValueAt("Comment", i, 1);
	    }

	}

	coloumnsTable = new JTable(tableModel);
	TableColumnAdjuster tca = new TableColumnAdjuster(coloumnsTable);
	tca.adjustColumns();

	coloumnsTableScrollPane = new JScrollPane(coloumnsTable);

	/* Deep copy table data vector to stack */
	Vector defaultDataVector = tableModel.getDataVector();
	memento.saveCurrentState(defaultDataVector);

    }

    /************************************************************************
     * InitTable method - end
     ************************************************************************/

    /************************************************************************
     * GetPanel method - start
     ************************************************************************/

    @Override
    public JPanel getPanel() {
	return userAssistedParserPanel;
    }
    /************************************************************************
     * GetPanel method - end
     ************************************************************************/
}
